// promise

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function promise(executor) {
  const self = this
  self.status = PENDING
  self.data = undefined
  self.callback = []

  function resolve(value) {
    if (self.status !== PENDING) return
    self.status = RESOLVED
    self.data = value
    if (self.callback.length > 0) {
      setTimeout(() => {
        self.callback.forEach(cbs => {
          cbs.onResolved(value)
        })
      })
    }
  }

  function reject(reason) {
    if (self.status !== PENDING) return
    self.status = REJECTED
    self.data = reason
    if (self.callback.length > 0) {
      setTimeout(() => {
        self.callback.forEach(cbs => {
          cbs.onRejected(reason)
        })
      })
    }
  }

  try {
    executor(resolve, reject)
  } catch (err) {
    reject(err)
  }
}

promise.prototype.then = function (onResolved, onResolved) {
  const self = this
  onResolved = typeof onResolved === 'function' ? onResolved : value => value
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {
    throw reason
  }
  return new promise((resolve, reject) => {

    function handler(callback) {
      try {
        const result = callback(self.data)
        if (result instanceof promise) {
          result.then(
            value => resolve(value),
            reason => reject(reason)
          )
        } else {
          resolve(result)
        }
      } catch (err) {
        reject(err)
      }
    }

    if (self.status === RESOLVED) {
      setTimeout(() => {
        handler(onResolved)
      })
    } else if (self.status === REJECTED) {
      setTimeout(() => {
        handler(onRejected)
      })
    } else {
      self.callback.push(
        {
          onResolved(value) {handler(onResolved)}
        },
        {
          onRejected(reason) {handler(onRejected)}
        }
      )
    }

  })
}

promise.prototype.catch = function(onRejected) {
  return this.then(undefined, onRejected)
}

promise.resolve = function(value) {
  return new promise((resolve, reject) => {
    if (value instanceof promise) {
      value.then(resolve, reject)
    } else {
      resolve(value)
    }
  })
}

promise.reject = function(reason) {
  return new promise((resolve, reject) => {
    reject(reason)
  })
}

promise.all = function(promises) {
  return new promise((resolve, reject) => {
    let resolveC = 0
    const values = new Array(promises.length)
    promises.forEach((p, index) => {
      p.then(value => {
        resolveC++
        values[index] = value
        if (resolveC === promises.length) {
          resolve(values)
        }
      }, 
      reason => {
        reject(reason)
      })
    })
  })
}

promise.race = function(promises) {
  return new promise((resolve, reject) => {
    promises.forEach(p => {
      p.then(resolve, reject)
    })
  })
}